﻿using System;
using System.Text;
using System.Globalization;

namespace Silverlight.SqlStatementEvaluator
{
    /// <summary>
    /// String part type
    /// </summary>
    internal enum StringPartType
    {
        And,
        Asc,
        Desc,
        Equal,
        Grater,
        GraterOrEqual,
        LeftBracket,
        Less,
        LessOrEqual,
        Like,
        Not,
        Or,
        RightBracket
    }

    /// <summary>
    /// Current property enumeration
    /// </summary>
    internal enum CurrentPropertyEnum
    {
        FieldName,
        Comparison,
        ParameterIndex,
        SortDirection
    }

    /// <summary>
    /// Holds Part data
    /// </summary>
    internal class StringPart
    {
        #region Data Fields
        /// <summary>
        /// "AddedToCollection" property data field
        /// </summary>
        private bool _addedToCollection;

        /// <summary>
        /// Holds current property
        /// </summary>
        private CurrentPropertyEnum _currentProperty;

        /// <summary>
        /// "FieldName" property data field
        /// </summary>
        private StringBuilder _fieldName;

        /// <summary>
        /// "NextPartText" property data field
        /// </summary>
        private StringBuilder _nextPartText;

        /// <summary>
        /// "Comparison" property data field
        /// </summary>
        private StringBuilder _comparison;

        /// <summary>
        /// "Ready" property data field
        /// </summary>
        private bool _ready;

        /// <summary>
        /// Sql statement part
        /// </summary>
        private SqlStatementPartEnum _sqlStatementPart;

        /// <summary>
        /// "Type" property data field
        /// </summary>
        private StringPartType _type;

        /// <summary>
        /// "Parameter" property data field
        /// </summary>
        private object _parameter;

        /// <summary>
        /// "ParameterIndex" property data field
        /// </summary>
        private StringBuilder _valueIndex;
        #endregion

        #region Constructors
        /// <summary>
        /// Default Constructor
        /// </summary>
        /// <param name="sqlStatementPart">Sql statement part type</param>
        public StringPart(SqlStatementPartEnum sqlStatementPart)
        {
            this._currentProperty = CurrentPropertyEnum.FieldName;
            this._fieldName = new StringBuilder();
            this._nextPartText = new StringBuilder();
            this._comparison = new StringBuilder();
            this._valueIndex = new StringBuilder();
            this._sqlStatementPart = sqlStatementPart;
        }
        #endregion

        #region Properties
        /// <summary>
        /// Gets or sets flag indicating whether part was added to collection
        /// </summary>
        public bool AddedToCollection
        {
            get
            {
                return this._addedToCollection;
            }
            set
            {
                this._addedToCollection = value;
            }
        }

        /// <summary>
        /// Sets character
        /// </summary>
        public char Character
        {
            set
            {
                if (Char.IsWhiteSpace(value))
                {
                    switch (this._sqlStatementPart)
                    {
                        case SqlStatementPartEnum.OrderBy:
                            // Use space as a signal for switching current property
                            switch (this._currentProperty)
                            {
                                case CurrentPropertyEnum.FieldName:
                                    // Switch to operation only if field name has some characters
                                    if (this._fieldName.Length > 0)
                                    {
                                        // Set Comparison as current property
                                        this.SwitchToComparison();
                                    }

                                    break;
                                case CurrentPropertyEnum.Comparison:
                                    // Set Ready only if field name has some characters
                                    if (this._comparison.Length > 0)
                                    {
                                        // Set flag, signalling that part is ready
                                        this.Ready = true;
                                    }

                                    break;
                            }

                            break;
                        case SqlStatementPartEnum.Select:
                            break;
                        case SqlStatementPartEnum.Where:
                            // Use space as a signal for switching current property
                            switch (this._currentProperty)
                            {
                                case CurrentPropertyEnum.FieldName:
                                    // Switch to operation only if field name has some characters
                                    if (this._fieldName.Length > 0)
                                    {
                                        // Set Comparison as current property
                                        this.SwitchToComparison();
                                    }

                                    break;
                                case CurrentPropertyEnum.Comparison:
                                    // Switch to index value only if operation has some characters
                                    if (this._comparison.Length > 0)
                                    {
                                        // Set Comparison as current property
                                        this.SwitchToParameterIndex();
                                    }

                                    break;
                                case CurrentPropertyEnum.ParameterIndex:
                                    // Set Ready only if field name has some characters
                                    if (this._valueIndex.Length > 0)
                                    {
                                        // Set flag, signalling that part is ready
                                        this.Ready = true;
                                    }
                                    break;
                            }

                            break;
                    }
                }
                else
                {
                    // Character is not a spase
                    switch (value)
                    {
                        case '<':
                            switch (this._sqlStatementPart)
                            {
                                case SqlStatementPartEnum.OrderBy:
                                    break;
                                case SqlStatementPartEnum.Select:
                                    break;
                                case SqlStatementPartEnum.Where:
                                    // Switch to current property to Comparison
                                    this.SwitchToComparison();

                                    if (this._ready)
                                    {
                                        this._nextPartText.Append(value);
                                    }
                                    else
                                    {
                                        this._comparison.Append(value);
                                    }

                                    break;
                            }

                            break;
                        case '>':
                            switch (this._sqlStatementPart)
                            {
                                case SqlStatementPartEnum.OrderBy:
                                    break;
                                case SqlStatementPartEnum.Select:
                                    break;
                                case SqlStatementPartEnum.Where:
                                    // Switch to current property to Comparison
                                    this.SwitchToComparison();

                                    if (this._ready)
                                    {
                                        this._nextPartText.Append(value);
                                    }
                                    else
                                    {
                                        this._comparison.Append(value);
                                    }

                                    break;
                            }

                            break;
                        case '=':
                            switch (this._sqlStatementPart)
                            {
                                case SqlStatementPartEnum.OrderBy:
                                    break;
                                case SqlStatementPartEnum.Select:
                                    break;
                                case SqlStatementPartEnum.Where:
                                    // Switch to current property to Comparison
                                    this.SwitchToComparison();

                                    if (this._ready)
                                    {
                                        this._nextPartText.Append(value);
                                    }
                                    else
                                    {
                                        this._comparison.Append(value);
                                    }

                                    break;
                            }

                            break;
                        case '(':
                            switch (this._sqlStatementPart)
                            {
                                case SqlStatementPartEnum.OrderBy:
                                    break;
                                case SqlStatementPartEnum.Select:
                                    break;
                                case SqlStatementPartEnum.Where:
                                    if (this._fieldName.Length == 0)
                                    {
                                        this._comparison.Append(value);
                                    }
                                    else
                                    {
                                        this._nextPartText.Append(value);
                                    }

                                    break;
                            }

                            this.Ready = true;
                            break;
                        case ')':
                            switch (this._sqlStatementPart)
                            {
                                case SqlStatementPartEnum.OrderBy:
                                    break;
                                case SqlStatementPartEnum.Select:
                                    break;
                                case SqlStatementPartEnum.Where:
                                    if (this._fieldName.Length == 0)
                                    {
                                        this._comparison.Append(value);
                                    }
                                    else
                                    {
                                        this._nextPartText.Append(value);
                                    }

                                    break;
                            }

                            this.Ready = true;
                            break;
                        case '@':
                            switch (this._sqlStatementPart)
                            {
                                case SqlStatementPartEnum.OrderBy:
                                    break;
                                case SqlStatementPartEnum.Select:
                                    break;
                                case SqlStatementPartEnum.Where:
                                    SwitchToParameterIndex();
                                    break;
                            }

                            break;
                        case ',':
                            switch (this._sqlStatementPart)
                            {
                                case SqlStatementPartEnum.OrderBy:
                                    if (this._fieldName.Length > 0)
                                    {
                                        this.Ready = true;
                                    }

                                    break;
                                case SqlStatementPartEnum.Select:
                                    break;
                                case SqlStatementPartEnum.Where:
                                    break;
                            }

                            break;
                        default:
                            // Alpha numeric character
                            // Add value to current property
                            switch (this._sqlStatementPart)
                            {
                                case SqlStatementPartEnum.OrderBy:
                                    switch (this._currentProperty)
                                    {
                                        case CurrentPropertyEnum.FieldName:
                                            // Add value to FieldName property
                                            this._fieldName.Append(value);
                                            break;
                                        case CurrentPropertyEnum.Comparison:
                                            if (this.ValidateSortCharacter(value))
                                            {
                                                this._comparison.Append(value);
                                            }
                                            else
                                            {
                                                if (this._comparison.Length > 0)
                                                {
                                                    this._nextPartText.Append(this._comparison.ToString());
                                                    this._comparison = new StringBuilder();
                                                }

                                                this._nextPartText.Append(value);
                                                this.Ready = true;
                                            }

                                            break;
                                    }

                                    break;
                                case SqlStatementPartEnum.Select:
                                    break;
                                case SqlStatementPartEnum.Where:
                                    switch (this._currentProperty)
                                    {
                                        case CurrentPropertyEnum.FieldName:
                                            // Add value to FieldName property
                                            this._fieldName.Append(value);
                                            break;
                                        case CurrentPropertyEnum.Comparison:
                                            if (this.ValidateComparisonCharacter(value))
                                            {
                                                this._comparison.Append(value);
                                            }
                                            else
                                            {
                                                if (this._comparison.Length > 0)
                                                {
                                                    this._nextPartText.Append(this._comparison.ToString());
                                                    this._comparison = new StringBuilder();
                                                }

                                                this._nextPartText.Append(value);
                                                this.Ready = true;
                                            }

                                            break;
                                        case CurrentPropertyEnum.ParameterIndex:
                                            // Add value to IndexValue property
                                            if (!Char.IsDigit(value))
                                            {
                                                throw new InvalidOperationException("ParameterIndex contains non-digit character: " + value);
                                            }

                                            this._valueIndex.Append(value);
                                            break;
                                    }
                                    break;
                            }

                            break;
                    }
                }
            }
        }

        /// <summary>
        /// Gets field name
        /// </summary>
        public string FieldName
        {
            get
            {
                return this._fieldName.ToString();
            }
            set
            {
                this._fieldName.Append(value);
            }
        }

        /// <summary>
        /// Gets next part text
        /// </summary>
        public string NextPartText
        {
            get
            {
                if (this._nextPartText.Length == 0)
                {
                    return null;
                }

                return this._nextPartText.ToString();
            }
        }

        /// <summary>
        /// Gets or sets part value
        /// </summary>
        public object Parameter
        {
            get
            {
                return this._parameter;
            }
            set
            {
                this._parameter = value;
            }
        }

        /// <summary>
        /// Gets value index
        /// </summary>
        public int ParameterIndex
        {
            get
            {
                if (this._valueIndex.Length == 0)
                {
                    return -1;
                }

                return int.Parse(this._valueIndex.ToString(), CultureInfo.InvariantCulture);
            }
        }

        /// <summary>
        /// Gets or sets Ready flag
        /// </summary>
        public bool Ready
        {
            get
            {
                return this._ready;
            }
            set
            {
                this._ready = value;
                if (this._ready)
                {
                    switch (this._sqlStatementPart)
                    {
                        case SqlStatementPartEnum.OrderBy:
                            break;
                        case SqlStatementPartEnum.Select:
                            break;
                        case SqlStatementPartEnum.Where:
                            if (this._fieldName.Length > 0 & this._comparison.Length == 0)
                            {
                                this._comparison.Append(this._fieldName.ToString());
                                this._fieldName = new StringBuilder();
                            }

                            break;
                    }

                    this.DetermineType();
                }
            }
        }

        /// <summary>
        /// Gets part type
        /// </summary>
        public StringPartType Type
        {
            get
            {
                return this._type;
            }
        }
        #endregion

        #region Methods
        // Determines part type
        private void DetermineType()
        {
            switch (this._sqlStatementPart)
            {
                case SqlStatementPartEnum.OrderBy:
                    if (this._comparison.Length == 0)
                    {
                        this._type = StringPartType.Asc;
                    }
                    else
                    {
                        switch (this._comparison.ToString())
                        {
                            case "ASC":
                                this._type = StringPartType.Asc;
                                break;
                            case "DESC":
                                this._type = StringPartType.Desc;
                                break;
                            default:
                                throw new InvalidOperationException("Unrecognized part type: " + this._fieldName.ToString());
                        }
                    }

                    break;
                case SqlStatementPartEnum.Select:
                    break;
                case SqlStatementPartEnum.Where:
                    switch (this._comparison.ToString())
                    {
                        case "AND":
                            this._type = StringPartType.And;
                            break;
                        case "=":
                            this._type = StringPartType.Equal;
                            break;
                        case ">":
                            this._type = StringPartType.Grater;
                            break;
                        case ">=":
                            this._type = StringPartType.GraterOrEqual;
                            break;
                        case "(":
                            this._type = StringPartType.LeftBracket;
                            break;
                        case "<":
                            this._type = StringPartType.Less;
                            break;
                        case "<=":
                            this._type = StringPartType.LessOrEqual;
                            break;
                        case "LIKE":
                            this._type = StringPartType.Like;
                            break;
                        case "NOT":
                            this._type = StringPartType.Not;
                            break;
                        case "OR":
                            this._type = StringPartType.Or;
                            break;
                        case ")":
                            this._type = StringPartType.RightBracket;
                            break;
                        default:
                            throw new InvalidOperationException("Unrecognized part type: " + this._fieldName.ToString());
                    }

                    break;
            }
        }

        /// <summary>
        /// Makes Comparison property as current
        /// </summary>
        private void SwitchToComparison()
        {
            // Field name must be presented
            if (this._fieldName.Length == 0)
            {
                throw new InvalidOperationException("Field name cannot be empty");
            }

            // Move reserved words into operation
            if (this._fieldName.ToString() == "AND"
                || this._fieldName.ToString() == "("
                || this._fieldName.ToString() == "NOT"
                || this._fieldName.ToString() == "OR"
                || this._fieldName.ToString() == ")"
                )
            {
                this._comparison.Append(this._fieldName.ToString());
                this._fieldName = new StringBuilder();
                this.Ready = true;
            }

            this._currentProperty = CurrentPropertyEnum.Comparison;
        }

        /// <summary>
        /// Makes ParameterIndex property as current
        /// </summary>
        private void SwitchToParameterIndex()
        {
            this._currentProperty = CurrentPropertyEnum.ParameterIndex;
        }

        /// <summary>
        /// Validates character for operation
        /// </summary>
        /// <param name="value">Character fro validation</param>
        /// <returns>True if character is valid</returns>
        private bool ValidateComparisonCharacter(char value)
        {
            if (this._comparison.Length == 0)
            {
                if (value == 'L')
                {
                    return true;
                }

                return false;
            }

            if (this._comparison.Length == 1)
            {
                if (value == 'I')
                {
                    return true;
                }

                return false;
            }

            if (this._comparison.Length == 2)
            {
                if (value == 'K')
                {
                    return true;
                }

                return false;
            }

            if (this._comparison.Length == 3)
            {
                if (value == 'E')
                {
                    return true;
                }

                return false;
            }

            return false;
        }

        /// <summary>
        /// Validates character for sort operation
        /// </summary>
        /// <param name="value">Character fro validation</param>
        /// <returns>True if character is valid</returns>
        private bool ValidateSortCharacter(char value)
        {
            if (this._comparison.Length == 0)
            {
                if (value == 'A' || value == 'D')
                {
                    return true;
                }

                return false;
            }

            if (this._comparison.Length == 1)
            {
                if (value == 'S' || value == 'E')
                {
                    return true;
                }

                return false;
            }

            if (this._comparison.Length == 2)
            {
                if (value == 'C' || value == 'S')
                {
                    return true;
                }

                return false;
            }

            if (this._comparison.Length == 3)
            {
                if (value == 'C')
                {
                    return true;
                }

                return false;
            }

            return false;
        }
        #endregion
    }
}
